<?php
/**
 * Field Ripper
 *
 * Rips fields (fields + relation structure) from edo objects. Field path is a path (relative from start obj)
 * containing zero or more relations and a fieldname. For ex. path to the edoid of the startobj would be "edoid",
 * path to a related objects edoid field would be "relation-edoid".
 * 
 * Another example: try to get the street address of persons workplace
 * startobject: person, path: company-address-streetname
 *
 * @package EDO
 * @subpackage Tools
 * @author Mikko Hämäläinen
 * @license LGPL
 */
class FieldRipper 
{
	
	protected $lead; 
	
	const EDO_PATH_SEPARATOR = "-";
		
	/**
	 * Banned relations, these are already processed.
	 *
	 * @var Array
	 */
	protected $bans = array();
	
	/**
	 * stack (?)
	 *
	 * @var Array 
	 */
	protected $stack = array();
	
	/**
	 * The depth we're currently in the node hierarchy
	 *
	 * @var unknown_type
	 */
	protected $nodelevel = 0;
	
	/**
	 * Enter description here...
	 *
	 * @param Application $lead
	 */
	public function __construct($lead)
	{
		$this->lead = $lead;
		$this->edo = $lead->edo;	
		LEAD::requirePackage("datastructures/stacks/BasicStack");
		$this->stack = new BasicStack();
	}
	
	
	public function ripIt($dao)
	{
		return $this->processEdo($dao);	
	}
	
	/**
	 * Resolves an $obj value from $path. Path is a id string (generated from rip), where
	 * format is startObject-relation-relation..-..-fieldName.
	 *
	 * @param EDO_Object_basic $obj Startobject
	 * @param string $path Path to value
	 * @return The value
	 */
	public static function resolve($obj, $path)
	{
		LEAD::requirePackage("/EDO/Tools/FieldPath");
		return FieldPath::resolve($obj, $path, self::EDO_PATH_SEPARATOR);
	}
	
	protected function processEdo($obj)
	{
		static $nodelevel = 0;
        $nodelevel++;
        
        #if(!$this->stack->size()) $this->stack->push($obj->getClass());
        
		$arrFields = array();
        $arrForeignKeys = array();
		
        $processFields = $obj->getFieldArray();
        
        foreach($processFields as $field)
        {
        	$res = false;
        	if  (is_a($field, "Datatype_relation_has_one")||is_a($field, "Datatype_relation_belongs_to"))
        	{
          		//--
        		$relation = $field;
        		if(!in_array($relation->getOwner()->getclass() . '_' . $relation->getName(), $this->bans))
		        {
		            $this->bans[] = $relation->getOwner()->getClass() . '_' . $relation->getName();
		        
		            if($opposite = $relation->getOpposite()) $this->bans[] = $opposite;
		            
		            $res = $this->getRelationInfo($field);
				}
				else
				{
					array_push($arrForeignKeys,$field->getForeignKey());
					continue;
				}
				
				array_push($arrForeignKeys,$field->getForeignKey());
        	}
        	else if(!is_a($field, "Datatype_relation")) // normal field with no link to
        	{
        		$res = $this->getNodeInfo($field);
        	}
        	else
        	{
        		$res = false;
        		continue;
        	}

        	
        	$arrFields[$field->getName()] = $res;
        	
        }
        
       	foreach($arrForeignKeys as $fk)
        {
        	$arrFields[$fk] = null;
        	unset($arrFields[$fk]);
        }
        #print_r($arrFields);
	    $nodelevel--;
	   # $arrFields = array_unique($arrFields);
	    return $arrFields;
	}
	
	
	protected function getNodeInfo($field)
	{
		$fieldName = $field->getName();
		$name = $this->lead->selkokielifyObjectsField($field->getOwner(), $fieldName);
		return array('name'=>$name,'id'=>$this->getIdForField($fieldName)/*$field->getOwner()->getClass()."_".$fieldName*/);
	}
	
	protected function getRelationInfo($relation)
	{       
            $this->stack->push($relation->getName());
            
            
			$dArr = array();
            $dArr['destination'] = $relation->getClassName();
            $dArr['id'] = $this->getIdForField();#$relation->getOwner()->getclass() . '_' . $relation->getName();
          	$dArr['name'] = $this->lead->selkokielifyObjectsField($relation->getOwner(), $relation->getName());
            $dArr['fields'] = $this->processEdo($this->edo->get($dArr['destination']));
            $this->stack->pop();
            return $dArr;
	}
	
	protected function getIdForField($field = "")
	{
		$stck = clone($this->stack);
		if($field)
		{
			$stck->push($field);
		}
		$path = implode(self::EDO_PATH_SEPARATOR, array_reverse($stck->popAll()));
		return $path;
	}
}